package com.geovis.emergency.spd.biz.system.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.geovis.common.core.constant.CommonConstants;
import com.geovis.common.core.entity.BaseEntity;
import com.geovis.common.file.dto.FileDTO;
import com.geovis.common.file.dto.UploadDTO;
import com.geovis.common.file.handler.IFileHandler;
import com.geovis.common.mybatis.page.PageParam;
import com.geovis.common.mybatis.page.PageResult;
import com.geovis.emergency.spd.biz.system.mapper.SystemFileBusinessMapper;
import com.geovis.emergency.spd.biz.system.mapper.SystemFileMapper;
import com.geovis.emergency.spd.biz.system.service.ISystemFileService;
import com.geovis.emergency.spd.entity.pc.pojo.vo.PcPlanDetailVO;
import com.geovis.emergency.spd.entity.system.constant.SystemFileBusinessConstant;
import com.geovis.emergency.spd.entity.system.entity.SystemFile;
import com.geovis.emergency.spd.entity.system.entity.SystemFileBusiness;
import com.geovis.emergency.spd.entity.system.pojo.dto.SystemFileDTO;
import com.geovis.emergency.spd.entity.system.pojo.dto.SystemFileQueryDTO;
import com.geovis.emergency.spd.entity.system.pojo.dto.SystemFileSaveDTO;
import com.geovis.emergency.spd.entity.system.pojo.dto.SystemFileUploadDTO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 附件表 服务实现类
 * </p>
 *
 * @author 曾德实
 * @since 2020-09-01
 */
@Service
@AllArgsConstructor
@Slf4j
public class SystemFileServiceImpl extends ServiceImpl<SystemFileMapper, SystemFile> implements ISystemFileService {

    private final SystemFileBusinessMapper systemFileBusinessMapper;

    private final IFileHandler fileHandler;

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public SystemFile upload(SystemFileUploadDTO uploadDTO) {
        FileDTO fileDTO = fileHandler.uploadFile(uploadDTO);

        SystemFile systemFile = BeanUtil.copyProperties(fileDTO, SystemFile.class);
        systemFile.setMd5(fileDTO.getIdentifier());
        SystemFileSaveDTO saveDTO = new SystemFileSaveDTO();
        saveDTO.setSystemFile(systemFile).setSystemFileBusiness(BeanUtil.toBean(uploadDTO, SystemFileBusiness.class));
        this.save(saveDTO);
        SystemFile mergeSystemFile = autoMerge(uploadDTO);
        if (ObjectUtil.isNotEmpty(mergeSystemFile)) {
            systemFile = mergeSystemFile;
        }
        return systemFile;
    }

    /**
     * 自动合并
     *
     * @param uploadDTO
     */
    private SystemFile autoMerge(SystemFileUploadDTO uploadDTO) {
        if (ObjectUtil.isEmpty(uploadDTO.getTotalChunks()) || uploadDTO.getTotalChunks() == 1 || !uploadDTO.getTotalChunks().equals(uploadDTO.getChunkNumber())) {
            return null;
        }

        List<SystemFile> list = baseMapper.selectList(new QueryWrapper<SystemFile>().lambda().eq(SystemFile::getMd5, uploadDTO.getIdentifier()).apply("size < total_size"));
        List<FileDTO> dtoList = list.stream().map(systemFile -> {
            FileDTO fileDTO = BeanUtil.copyProperties(systemFile, FileDTO.class);
            fileDTO.setIdentifier(systemFile.getMd5());
            return fileDTO;
        }).collect(Collectors.toList());
        FileDTO fileDTO = fileHandler.merge(dtoList);
        SystemFile systemFile = BeanUtil.copyProperties(fileDTO, SystemFile.class);
        systemFile.setMd5(fileDTO.getIdentifier());
        return mergeChunk(list, systemFile);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void save(SystemFileSaveDTO dto) {
        SystemFile systemFile = dto.getSystemFile();
        SystemFileBusiness systemFileBusiness = dto.getSystemFileBusiness();
        if (ObjectUtil.isEmpty(systemFile)) {
            return;
        }
        // 1、附件保存入表
        systemFile.setDataStatus(CommonConstants.YES);
        baseMapper.insert(systemFile);

        if (ObjectUtil.isEmpty(systemFileBusiness.getParam1()) && ObjectUtil.isEmpty(systemFileBusiness.getParam2())
                && ObjectUtil.isEmpty(systemFileBusiness.getParam3()) && ObjectUtil.isEmpty(systemFileBusiness.getParam4())) {
            return;
        }
        // 2、业务关联表保存  （表结构修改）
        if (ObjectUtil.isNotEmpty(systemFileBusiness)) {
            systemFileBusiness.setFileId(systemFile.getId());
            systemFileBusinessMapper.insert(systemFileBusiness);
        }
    }

    @Override
    public void saveFromFiling(PcPlanDetailVO detailVO) {
        detailVO.getPlaningFileList().stream().forEach(file->saveFile(file));
        detailVO.getOperationManualFileList().stream().forEach(file->saveFile(file));
        detailVO.getOverViewFileList().stream().forEach(file->saveFile(file));
        detailVO.getOtherFileList().stream().forEach(file->saveFile(file));

    }

    private void saveFile(SystemFile file)
    {
        file.setId(null);
        this.save(file);
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public SystemFile mergeChunk(List<SystemFile> list, SystemFile systemFile) {
        //1、删除文件表
        List<String> idList = list.stream().map(BaseEntity::getId).collect(Collectors.toList());
        baseMapper.deleteBatchIds(idList);

        //2、删除文件业务关联表
        SystemFileBusiness systemFileBusiness = null;
        List<SystemFileBusiness> systemFileBusinessList = systemFileBusinessMapper.selectList(new QueryWrapper<SystemFileBusiness>().lambda().in(SystemFileBusiness::getFileId, idList));
        if (CollUtil.isNotEmpty(systemFileBusinessList)) {
            systemFileBusiness = BeanUtil.copyProperties(systemFileBusinessList.get(0), SystemFileBusiness.class);
        }
        systemFileBusinessMapper.delete(new UpdateWrapper<SystemFileBusiness>().lambda().in(SystemFileBusiness::getFileId, idList));

        //3、保存新的合并文件
        SystemFileSaveDTO dto = new SystemFileSaveDTO();
        dto.setSystemFile(systemFile);
        dto.setSystemFileBusiness(systemFileBusiness);
        save(dto);
        return dto.getSystemFile();
    }

    @Override
    public PageResult<SystemFile> listPage(PageParam<SystemFileQueryDTO> pageParam) {
        SystemFileQueryDTO queryDTO = pageParam.getQuery();
        IPage<SystemFile> page = pageParam.buildPage();
        QueryWrapper<Object> wrapper = buildWrapper(queryDTO);
        baseMapper.selectByPage(page, wrapper);
        return new PageResult<SystemFile>(page);
    }

    @Override
    public List<SystemFileDTO> getList(SystemFileQueryDTO queryDTO) {
        return baseMapper.selectByList(buildWrapper(queryDTO));
    }

    @Override
    public List<SystemFile> getListById(String relatedId, String type) {
        SystemFileQueryDTO systemFileQueryDTO = new SystemFileQueryDTO();
        systemFileQueryDTO.setRelatedId(relatedId);
        systemFileQueryDTO.setType(type);
        List<SystemFileDTO> list = this.getList(systemFileQueryDTO);
        return list.stream().map(systemFileDTO -> {
            SystemFile systemFile = new SystemFile();
            BeanUtil.copyProperties(systemFileDTO, systemFile);
            return systemFile;
        }).collect(Collectors.toList());
    }

    private QueryWrapper<Object> buildWrapper(SystemFileQueryDTO queryDTO) {
        QueryWrapper<Object> wrapper = Wrappers.query();
        if (ObjectUtil.isEmpty(queryDTO)) {
            return wrapper;
        }
        wrapper.eq(ObjectUtil.isNotEmpty(queryDTO.getType()), "b.type", queryDTO.getType())
                .eq(ObjectUtil.isNotEmpty(queryDTO.getRelatedId()), "b.related_id", queryDTO.getRelatedId())
                .eq(ObjectUtil.isNotEmpty(queryDTO.getParam1()), "b.param1", queryDTO.getParam1())
                .eq(ObjectUtil.isNotEmpty(queryDTO.getParam2()), "b.param2", queryDTO.getParam2())
                .eq(ObjectUtil.isNotEmpty(queryDTO.getParam3()), "b.param3", queryDTO.getParam3())
                .eq(ObjectUtil.isNotEmpty(queryDTO.getParam4()), "b.param4", queryDTO.getParam4())
                .eq("b.is_deleted", "0")
                .eq("a.is_deleted", "0")
                .in(ObjectUtil.isNotEmpty(queryDTO.getRelatedIdList()), "b.related_id", queryDTO.getRelatedIdList());
        return wrapper;
    }


    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void removeByIdList(Set<String> idList) {
        // 1、删除附件关联
        systemFileBusinessMapper.delete(new UpdateWrapper<SystemFileBusiness>().lambda().in(SystemFileBusiness::getFileId, idList));

        List<SystemFile> list = listByIds(idList);
        // 2、删除磁盘附件
        list.forEach(item -> {
            Long fileCount = count(Wrappers.lambdaQuery(SystemFile.class).eq(SystemFile::getMd5, item.getMd5()));
            try {
                // 删除条件
                final int deleteCondition = 2;
                if (fileCount < deleteCondition) {
                    Files.deleteIfExists(Paths.get(item.getDir() + item.getPath()));
                }
            } catch (IOException e) {
                log.error(StrUtil.format("删除附件异常->{}", item.getName()));
            }
        });

        //3、删除附件表
        super.removeByIds(idList);
    }

    @Override
    public void linkBusiness(List<String> fileIdList, String... param) {
        if(ObjectUtil.isEmpty(fileIdList) || ObjectUtil.isEmpty(param)){
            return;
        }
        fileIdList.forEach(fileId -> {
            SystemFileBusiness systemFileBusiness = new SystemFileBusiness();
            systemFileBusiness.setFileId(fileId);
            for (int i = 0; i < param.length; i++) {
                BeanUtil.setFieldValue(systemFileBusiness, "param".concat(String.valueOf(i + 1)), param[i]);
            }
            systemFileBusinessMapper.insert(systemFileBusiness);
        });
    }
    @Override
    public void linkBusiness(List<SystemFile> fileList, String relatedId, String type){
        if (ObjectUtil.isNotEmpty(fileList)){
            List<String> fileIdList = fileList.stream().map(SystemFile::getId).filter(StrUtil::isNotBlank).collect(Collectors.toList());
            //将已经前端已经删除的文件删除掉
            if (ObjectUtil.isNotEmpty(fileIdList)){
                LambdaQueryWrapper<SystemFileBusiness> updateWrapper = Wrappers.<SystemFileBusiness>lambdaQuery()
                        .eq(SystemFileBusiness::getRelatedId, relatedId)
                        .eq(SystemFileBusiness::getType, type)
                        .notIn(SystemFileBusiness::getFileId, fileIdList);
                systemFileBusinessMapper.delete(updateWrapper);
            }

            Set<SystemFileBusiness> fileBusinessSet = CollUtil.set(false);
            //关联业务附件
            fileList.forEach(systemFile -> {
                if (ObjectUtil.isNotEmpty(fileIdList)){
                    if (systemFileBusinessMapper.selectCount(Wrappers.<SystemFileBusiness>lambdaQuery()
                            .eq(SystemFileBusiness::getFileId,systemFile.getId())
                            .eq(SystemFileBusiness::getRelatedId, relatedId)
                            .eq(SystemFileBusiness::getType, type)) == 0){
                        fileBusinessSet.add(new SystemFileBusiness(systemFile.getId(), type, relatedId));
                    }
                }else {
                    fileBusinessSet.add(new SystemFileBusiness(systemFile.getId(), type, relatedId));
                }
            });
            for (SystemFileBusiness systemFileBusiness : fileBusinessSet) {
                systemFileBusinessMapper.insert(systemFileBusiness);
            }
        }else {
            if (StrUtil.isAllNotBlank(relatedId,type)) {
                LambdaQueryWrapper<SystemFileBusiness> updateWrapper = Wrappers.<SystemFileBusiness>lambdaQuery()
                        .eq(SystemFileBusiness::getRelatedId, relatedId)
                        .eq(SystemFileBusiness::getType, type);
                systemFileBusinessMapper.delete(updateWrapper);
            }
        }
    }
    @Override
    public void removeByParam(String... param) {
        if (ObjectUtil.isEmpty(param)) {
            return;
        }
        SystemFileQueryDTO queryDTO = new SystemFileQueryDTO();
        for (int i = 0; i < param.length; i++) {
            BeanUtil.setFieldValue(queryDTO, "param".concat(String.valueOf(i + 1)), param[i]);
        }
        List<SystemFileDTO> list = getList(queryDTO);
        Set<String> removeIdList = list.stream().map(SystemFile::getId).collect(Collectors.toSet());
        removeByIdList(removeIdList);
    }

    @Override
    public boolean checkUpload(UploadDTO dto) {
        final long minTotalChunks = 2;
        if (dto.getTotalChunks() < minTotalChunks) {
            return false;
        }
        Long uploadSize = baseMapper.selectCount(Wrappers.lambdaQuery(SystemFile.class).eq(SystemFile::getMd5, dto.getIdentifier()).apply("size < total_size"));
        return dto.getTotalChunks().equals(uploadSize);
    }

    @Override
    public SystemFile generateThumbnail(SystemFile systemFile, int width, int height) {
        if (ObjectUtil.isEmpty(systemFile)) {
            return null;
        }
        String srcImageFilePath = systemFile.getDir() + systemFile.getPath();
        BufferedImage image = ImgUtil.read(srcImageFilePath);
        if (ObjectUtil.isEmpty(image)) {
            return null;
        }
        // 图片压缩临时路径
        String destImageFilePath = StrUtil.subBefore(srcImageFilePath, "/", true) + "/" + IdWorker.getIdStr() + StrUtil.DOT + systemFile.getExtName();

        // 图片压缩
        ImgUtil.scale(new File(srcImageFilePath), new File(destImageFilePath), width, height, null);

        // 生成文件MD5
        String md5 = DigestUtil.md5Hex(new File(destImageFilePath));

        // 重名称
        FileUtil.rename(new File(destImageFilePath), md5 + StrUtil.DOT + systemFile.getExtName(), true);

        // 跟换文件名称后更换路径
        destImageFilePath = StrUtil.subBefore(srcImageFilePath, "/", true) + "/" + md5 + StrUtil.DOT + systemFile.getExtName();

        // 拼接相应的数据
        SystemFile result = BeanUtil.copyProperties(systemFile, SystemFile.class, "id");
        result.setName(systemFile.getName() + "(缩略图)")
                .setSize(FileUtil.size(FileUtil.file(destImageFilePath)))
                .setTotalSize(result.getSize())
                .setMd5(md5)
                .setPath(StrUtil.removePrefix(destImageFilePath, result.getDir()));
        this.save(result);
        return result;
    }
}
